
import java.io.FileWriter;
import java.io.PrintWriter;
import java.sql.DriverManager;
import java.util.*;

public class NegaMaxBot {
    // The DoTurn function is where your code goes. The PlanetWars object
    // contains the state of the game, including information about all planets
    // and fleets that currently exist. Inside this function, you issue orders
    // using the pw.IssueOrder() function. For example, to send 10 ships from
    // planet 3 to planet 8, you would say pw.IssueOrder(3, 8, 10).
    //
    // There is already a basic strategy in place here. You can use it as a
    // starting point, or you can throw it out entirely and replace it with
    // your own. Check out the tutorials and articles on the contest website at
    // http://www.ai-contest.com/resources.

    public static long _startTime = 0;
    public static int _currDepth = 10;

    public static void DoTurn(PlanetWars pw) throws Exception {
          _startTime = System.nanoTime();
          DriverManager.setLogWriter(new PrintWriter(new FileWriter(".\\outputfile.txt", true)));
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"DoMove - " + _startTime);
        

        // (1)Insert # of active fleets limit here
        if (pw.MyFleets().size() >= 1) {
            return;
        }
        Move nextMove = FindBestMove(pw, _currDepth);
       out.println("# "+"move duration: " + (System.nanoTime() - _startTime));
       out.println("# "+"########### MADE A FRIGGIN' MOVE!!!!! #####################");
       out.flush();
        // (4) Issue the order
        if (nextMove != null) {
            pw.IssueOrder(
                    nextMove.SourceID(),
                    nextMove.DestID(),
                    nextMove.NumShips());
        }

    }

    //Root negamax
    public static Move FindBestMove(PlanetWars pw, int depth) {
        long startTime = System.nanoTime();
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"GetScore - " + startTime);
        double max = Double.MIN_VALUE;
        double score = Double.MIN_VALUE;
        Move best = null;
        for (Move m : GetMoves(pw)) {
            score = negamax(pw, depth, Double.MIN_VALUE, Double.MAX_VALUE, 1);
            if (score > max) {
                max = score;
                best = m;
            }
        }
        if(best == null){
            out.println("#FindBestMove returned null");
        }
        return best;
    }

    //Recursive search
    public static double negamax(
            PlanetWars node,
            int depth,
            double alpha,
            double beta,
            int color) {
        long startTime = System.nanoTime();
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"negamax depth: " + depth + " - " +startTime);
        out.flush();
        if(depth < _currDepth)_currDepth = depth;
        
        if (depth == 0 || (System.nanoTime() - _startTime >= 800000)) {
            out.println("# "+"endtime " + (System.nanoTime() - _startTime));
//            out.println("# "+"**TERMINAL negamax " + depth);
            out.flush();
            return color * GetScore(node);
        } else {
            //iterate possible moves
            for (Move m : GetMoves(node)) {
                PlanetWars childNode = SimulateMove(node, m);
                alpha = Math.max(alpha, -1 * negamax(
                        childNode,
                        depth - 1,
                        -beta,
                        -alpha,
                        -color));
                // alpha beta pruning
                if (alpha >= beta) {
                    return alpha;
                }
            }
        }
        out.println("# "+"endtime "+(System.nanoTime() - startTime));
        out.println("# "+"**End negamax " + depth + " alpha: " + alpha);
        out.flush();
        return alpha;
    }

    //Scoring Heuristic
    public static double GetScore(PlanetWars node){
        long startTime = System.nanoTime();
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"GetScore - " + startTime);
        out.flush();
        
        int fleetScore = 0;
        for (Fleet f : node.MyFleets()) {
            fleetScore += f.NumShips() - f.TurnsRemaining();
        }
        double score = node.Production(1)
                + node.MyPlanets().size()
                + node.NumShips(1)
                + fleetScore;
        out.println("# "+"GetScore took " + (System.nanoTime() - startTime));
        out.println("# "+"*End GetScore score: " + score);
        out.flush();
        return score;
    }

    //Move List Generator
    public static List<Move> GetMoves(PlanetWars node) {
        long startTime = System.nanoTime();
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"GetMoves - " + startTime);
        out.flush();
        List<Move> m = new ArrayList<Move>();
        int source = 0;
        int sourceShips = 0;
        double sourceScore = 0;
        List<Planet> s = node.MyPlanets();
        List<Planet> d = node.NotMyPlanets();

        
        out.println("# "+"Setting source - " + (System.nanoTime() - startTime));
        out.flush();
        //send from strongest planet
        for(Planet p : s){
            double score = (double)p.NumShips() / (1 + p.GrowthRate());
            if(score > sourceScore){
                source = p.PlanetID();
                sourceScore = score;
                sourceShips = p.NumShips();
            }
        }
        out.println("# "+"Setting dest - " + (System.nanoTime() - startTime));
        out.flush();
        //target weakest planets
        for(Planet p : d){
            //double score = (double)p.NumShips() / (1 + p.GrowthRate());
            //if(score < sourceScore && node.Distance(source, p.PlanetID()) < _currDepth ){
                m.add(new Move(source,p.PlanetID(), (int)Math.ceil(sourceShips / 2)));
           // }
            
        }

        out.println("# GetMoves took  " + (System.nanoTime() - startTime));
        out.println("# "+"MoveCount: " + m.size());
        out.flush();
        return m;
    }

    // Updates a game state by one turn with given move
    public static PlanetWars SimulateMove(PlanetWars node, Move m) {
        long startTime = System.nanoTime();
        PrintWriter out = DriverManager.getLogWriter();
        out.println("# "+"SimulateMove - " + startTime);
        out.println("# "+(startTime));
        PlanetWars pw = (PlanetWars) node;
        pw.GetPlanet(m.DestID()).AddShips(m.NumShips());
        pw.GrowPlanets();
        pw.MoveFleets();

        //create a fleet and remove the ships from the source planet
        int moveDistance = pw.Distance(m.SourceID(), m.DestID());
        pw.Fleets().add(new Fleet(
                1,
                m.NumShips(),
                m.SourceID(),
                m.DestID(),
                moveDistance,
                moveDistance));
        pw.GetPlanet(m.SourceID()).RemoveShips(m.NumShips());

        out.println("# SimulateMove took  " + (System.nanoTime() - startTime));
        out.flush();
        return pw;
    }

    public static void main(String[] args) {
        String line = "";
        String message = "";
        int c;
        try {
            while ((c = System.in.read()) >= 0) {
                switch (c) {
                    case '\n':
                        if (line.equals("go")) {
                            PlanetWars pw = new PlanetWars(message);
                            DoTurn(pw);
                            pw.FinishTurn();
                            message = "";
                        } else {
                            message += line + "\n";
                        }
                        line = "";
                        break;
                    default:
                        line += (char) c;
                        break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
            // Owned.
        }
    }
}
